package top.healthylife.gateway.platform.softwind.api.api.aggregationapi;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.healthylife.gateway.core.service.ParentFace;
import top.healthylife.gateway.dto.annotation.ApiServiceExplain;
import top.healthylife.gateway.platform.softwind.api.api.enterpriseinvoice.WindInvoiceConsolidationApi;
import top.healthylife.gateway.platform.softwind.dto.basedto.req.BuildDtoUtil;
import top.healthylife.gateway.platform.softwind.dto.enterpriseinvoice.req.WindReqInvoiceConsolidationListDto;
import top.healthylife.gateway.platform.softwind.dto.enterpriseinvoice.res.WindResInvoiceConsolidationListDto;
import top.healthylife.gateway.platform.softwind.dto.enterpriseinvoice.res.WindResInvoiceConsolidationListItem;
import top.healthylife.gateway.platform.softwind.dto.res.WindResAggregationDto;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author : GaoZeXi by {@link BuildDtoUtil}
 * @date 2021/7/14 17:47
 */
@Slf4j
@Service
@ApiServiceExplain(docUrl = "", price = 20, apiName = "数据聚合接口", info = "数据聚合接口,业务可直接调用,数据来源于[发票合并接口]接口")
public class WindAggregationDataApi implements ParentFace<WindReqInvoiceConsolidationListDto, WindResAggregationDto> {

    @Autowired
    WindInvoiceConsolidationApi windInvoiceConsolidationApi;


    @Override
    public WindResAggregationDto execute(WindReqInvoiceConsolidationListDto softWindUnifyReqDto) throws Exception {
        //1.通过数据网关调用接口
        WindResInvoiceConsolidationListDto windResInvoiceConsolidationListDto = windInvoiceConsolidationApi
                .executeByApiGateway(softWindUnifyReqDto);
        List<WindResInvoiceConsolidationListItem> result = windResInvoiceConsolidationListDto.getResult();

        WindResAggregationDto windResAggregationDto = new WindResAggregationDto();
        List<String> kprqList = getKprqList(result);
        //最早开票日期
        windResAggregationDto.setLastInvoicingDate(kprqList.get(kprqList.size() - 1));
        //最近开票日期
        windResAggregationDto.setLastInvoicingDate(kprqList.get(0));
        kprqList = result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign())
                        && "非作废".equals(e.getZfbz()))
                .map(WindResInvoiceConsolidationListItem::getKprq)
                .sorted()
                .collect(Collectors.toList());
        //近12月最大开票时间间隔天数（自然月)
        windResAggregationDto.setInvoicingTimeIntervalOverLast12Months(Math.toIntExact(getMaxInterval(kprqList, 12)));
        // 近12月无开票月份数（自然月）
        List<String> billingTimes = getBillingTimes(kprqList, 12);
        windResAggregationDto.setNoInvoicingTimesOverLast12Months(12 - billingTimes.size());
        //近12/6/3月销项发票有效含税金额合计
        windResAggregationDto.setValidTaxIncludedAmountOfSalesInvoiceOverLast12Months(getEffectiveBillingMoney(result, 12));
        windResAggregationDto.setValidTaxIncludedAmountOfSalesInvoiceOverLast6Months(getEffectiveBillingMoney(result, 6));
        windResAggregationDto.setValidTaxIncludedAmountOfSalesInvoiceInLast3Months(getEffectiveBillingMoney(result, 3));
        //下游客户
        windResAggregationDto.setDownstreamCustomersOverLast12Months(getDownstreamCustomers(result, 12));
        //近3月前3大供应商开票金额占比
        windResAggregationDto.setThreeMajorSuppliersInvoicingMoneyProportionOverLast3Months(getThreeMajorSuppliersInvoicingMoneyProportion(result, 3));
        //近3月销项发票有效含税金额同比增长率 = （sign为销项，zfbz为非作废，开票日期kprq在近3个自然月的所有发票数据中对jshj（价税合计）求汇总值）/（sign为销项，zfbz为非作废，开票日期kprq在1年前的3个自然月的所有发票数据中对jshj（价税合计）求汇总值）
        // fixme 提供的计算方法有问题
        // 此处使用等量换算 为: 近三月-(同年三月)/同年三月              注:同年三月=近15月-近12月
        windResAggregationDto.setValidTaxIncludedAmountOfSalesInvoiceYearOnYearGrowthRateOverLast3Months(getYearOnYearGrowthRate(result, 3));
        windResAggregationDto.setValidTaxIncludedAmountOfSalesInvoiceYearOnYearGrowthRateOverLast6Months(getYearOnYearGrowthRate(result, 6));
        windResAggregationDto.setNumberOfValidSalesInvoicesOverLast3Months(Math.toIntExact(getNumberOfValidSalesInvoices(result, 3)));
        windResAggregationDto.setNumberOfValidSalesInvoicesOverLast6Months(Math.toIntExact(getNumberOfValidSalesInvoices(result, 6)));
        windResAggregationDto.setNumberOfInstitutionsWithBuyerTaxIdIsStartWith11Or12InLast12Months(
                Math.toIntExact(
                        getInstitutionsWithBuyerTaxIdIsStartWith11Or12InLast12Months(result, 12).stream().distinct().count()
                )
        );
        windResAggregationDto.setTimesOfBuyerTaxIdIsStartWith11Or12InLast12Months(
                getInstitutionsWithBuyerTaxIdIsStartWith11Or12InLast12Months(result, 12).size()
        );
        return windResAggregationDto;
    }

    /**
     * 近n月销项发票中购方税号前2位为11、12的机构出现次数(未去重)（自然月）
     *
     * @param result 结果集
     * @param n      近n个月
     * @return 近n月销项发票中购方税号前2位为11、12的机构出现次数(未去重)
     */
    public List<String> getInstitutionsWithBuyerTaxIdIsStartWith11Or12InLast12Months(List<WindResInvoiceConsolidationListItem> result, int n) {
        DateTime now = DateTime.now();
        DateTime offset = now.offsetNew(DateField.MONTH, n);
        Predicate<WindResInvoiceConsolidationListItem> predicate = e ->
                DateUtil.parseDate(e.getKprq()).isIn(offset, now) &&
                        (e.getGfsh().startsWith("11") || e.getGfsh().startsWith("12"));
        return result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign())
                        && "非作废".equals(e.getZfbz()))
                .filter(predicate).map(WindResInvoiceConsolidationListItem::getGfsh).collect(Collectors.toList());
    }

    /**
     * 近n月销项发票有效张数（自然月）
     *
     * @param result 结果集
     * @param n      近n个月
     * @return 销项发票有效张数
     */
    public long getNumberOfValidSalesInvoices(List<WindResInvoiceConsolidationListItem> result, int n) {
        DateTime now = DateTime.now();
        DateTime offset = now.offsetNew(DateField.MONTH, n);
        result = result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign())
                        && "非作废".equals(e.getZfbz()))
                .filter(e -> DateUtil.parseDate(e.getKprq()).isIn(offset, now)).collect(Collectors.toList());
        return result.stream().map(WindResInvoiceConsolidationListItem::getFphm).distinct().count();
    }

    /**
     * 近n月销项发票有效含税金额同比增长率
     *
     * @param result 结果集
     * @param n      近n个月
     * @return 近n月销项发票有效含税金额同比增长率
     */
    public Double getYearOnYearGrowthRate(List<WindResInvoiceConsolidationListItem> result, int n) {
        //近三月
        Double effectiveBillingMoney = getEffectiveBillingMoney(result, n);
        //同年三月
        Double sameTimeLastYearBillingMoney = getEffectiveBillingMoney(result, 12 + n) - getEffectiveBillingMoney(result, 12);

        double sub = NumberUtil.sub(effectiveBillingMoney, sameTimeLastYearBillingMoney);
        return NumberUtil.div((Double) NumberUtil.mul(sub, 100), sameTimeLastYearBillingMoney);
    }

    /**
     * 近n月前3大供应商开票金额占比
     *
     * @param result 结果集
     * @param n      近n个月
     * @return 近n月前3大供应商开票金额占比
     */
    public Double getThreeMajorSuppliersInvoicingMoneyProportion(List<WindResInvoiceConsolidationListItem> result, int n) {
        DateTime now = DateTime.now();
        DateTime offset = now.offsetNew(DateField.MONTH, n);
        result = result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign())
                        && "非作废".equals(e.getZfbz()))
                .filter(e -> DateUtil.parseDate(e.getKprq()).isIn(offset, now)).collect(Collectors.toList());
        Double total = result.stream().map(e -> Double.parseDouble(e.getJshj())).reduce(Double::sum).orElse(0.0);


        Map<String, Optional<Double>> map = result.stream().collect(
                Collectors.groupingBy(
                        WindResInvoiceConsolidationListItem::getGfsh,
                        Collectors.mapping(e -> Double.parseDouble(e.getJshj()), Collectors.reducing(Double::sum))));
        List<Double> list = map.values().stream().map(e -> e.orElse(0.0)).sorted().collect(Collectors.toList());
        Collections.reverse(list);
        Double sum = list.stream().limit(3).reduce(Double::sum).orElse(0.0);
        return NumberUtil.div(sum, total) * 100.0;
    }

    /**
     * 近n月下游客户数（自然月）
     *
     * @param result 结果集
     * @param n      近n个月
     * @return 近n月下游客户数
     */
    public int getDownstreamCustomers(List<WindResInvoiceConsolidationListItem> result, int n) {
        DateTime now = DateTime.now();
        DateTime offset = now.offsetNew(DateField.MONTH, n);
        long count = result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign())
                        && "非作废".equals(e.getZfbz()))
                .filter(e -> DateUtil.parseDate(e.getKprq()).isIn(offset, now)).distinct().count();
        return Math.toIntExact(count);
    }

    /**
     * 近n月销项发票有效含税金额合计（自然月）
     *
     * @param result 结果集
     * @param n      近n个月
     * @return 近n月销项发票有效含税金额合计
     */
    public Double getEffectiveBillingMoney(List<WindResInvoiceConsolidationListItem> result, int n) {
        DateTime now = DateTime.now();
        DateTime offset = now.offsetNew(DateField.MONTH, n);
        return result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign())
                        && "非作废".equals(e.getZfbz()))
                .map(e -> {
                    if (DateUtil.parseDate(e.getKprq()).isIn(offset, now)) {
                        return NumberUtil.parseNumber(e.getJshj());
                    } else {
                        return 0;
                    }
                }).reduce(0, NumberUtil::add).doubleValue();
    }

    /**
     * 获取所有开票日期列表
     *
     * @param result 开票日期列表
     * @return
     */
    public List<String> getKprqList(List<WindResInvoiceConsolidationListItem> result) {
        return result.stream().filter(e ->
                ObjectUtil.isNotNull(e.getKprq())
                        && "销项".equals(e.getSign()))
                .map(WindResInvoiceConsolidationListItem::getKprq)
                .sorted()
                .collect(Collectors.toList());
    }

    /**
     * 近n月最大开票时间间隔天数（自然月)
     *
     * @param kprqList 所有开票日期列表
     * @param n        n
     * @return 最大开票时间间隔天数
     */
    public long getMaxInterval(List<String> kprqList, int n) {
        //过滤掉不符合条件的
        DateTime now = DateTime.now();
        DateTime dateTime = now.offsetNew(DateField.MONTH, -n);
        kprqList = kprqList.stream().filter(e -> DateUtil.parseDate(e).isIn(dateTime, now)
        ).collect(Collectors.toList());
        long interval = 0;
        for (int i = 0; i < kprqList.size() - 1; i++) {
            String start = kprqList.get(i);
            String next = kprqList.get(i + 1);
            DateTime startTime = DateUtil.parseDate(start);
            DateTime nextTime = DateUtil.parseDate(next);
            long between = nextTime.between(startTime, DateUnit.DAY);
            if (between > 0) {
                interval = between;
            }
        }
        return interval;
    }

    /**
     * 近n月有开票记录的
     *
     * @param kprqList 开票日期列表
     * @param n        月份
     * @return 近n月有开票记录的
     */
    public List<String> getBillingTimes(List<String> kprqList, int n) {
        DateTime now = DateTime.now();
        List<String> kprqOverLast12Moth = new ArrayList<>();
        for (int j = 1; j <= n; j++) {
            DateTime offsetDate = now.offsetNew(DateField.MONTH, -j);
            for (int i = 0; i < kprqList.size(); i++) {
                DateTime targetTime = DateUtil.parseDate(kprqList.get(i));
                if (targetTime.isIn(offsetDate, now)) {
                    kprqOverLast12Moth.add(kprqList.remove(i));
                }
            }
        }
        return kprqOverLast12Moth;
    }

    //public static void main(String[] args) {
    //    List<WindResInvoiceConsolidationListItem> result = new ArrayList<>();
    //    WindResInvoiceConsolidationListItem windResInvoiceConsolidationListItem = new WindResInvoiceConsolidationListItem();
    //    windResInvoiceConsolidationListItem.setKprq("2021-01-01");
    //
    //    WindResInvoiceConsolidationListItem windResInvoiceConsolidationListItem2 = new WindResInvoiceConsolidationListItem();
    //    windResInvoiceConsolidationListItem2.setKprq("2021-02-01");
    //
    //    WindResInvoiceConsolidationListItem windResInvoiceConsolidationListItem3 = new WindResInvoiceConsolidationListItem();
    //    windResInvoiceConsolidationListItem3.setKprq("2021-01-03");
    //
    //    WindResInvoiceConsolidationListItem windResInvoiceConsolidationListItem4 = new WindResInvoiceConsolidationListItem();
    //    windResInvoiceConsolidationListItem4.setKprq("2021-04-01");
    //
    //    result.add(windResInvoiceConsolidationListItem);
    //    result.add(windResInvoiceConsolidationListItem2);
    //    result.add(windResInvoiceConsolidationListItem3);
    //    result.add(windResInvoiceConsolidationListItem4);
    //    List<String> collect = result.stream().map(WindResInvoiceConsolidationListItem::getKprq).filter(ObjectUtil::isNotEmpty).sorted().collect(Collectors.toList());
    //    long interval = 0;
    //    for (int i = 0; i < collect.size() - 1; i++) {
    //        String start = collect.get(i);
    //        String next = collect.get(i + 1);
    //        DateTime startTime = DateUtil.parseDate(start);
    //        DateTime nextTime = DateUtil.parseDate(next);
    //        long between = nextTime.between(startTime, DateUnit.DAY);
    //        if (between > 0) {
    //            interval = between;
    //        }
    //    }
    //
    //    DateTime now = DateTime.now();
    //    int noBillingTimes = 0;
    //    int billingTimes = 0;
    //    for (int j = 1; j <= 12; j++) {
    //        DateTime offsetDate = now.offsetNew(DateField.MONTH, -j);
    //        boolean notHit = false;
    //        for (int i = 0; i < collect.size(); i++) {
    //            DateTime targetTime = DateUtil.parseDate(collect.get(i));
    //            if (targetTime.isIn(offsetDate, now)) {
    //                notHit = true;
    //                collect.remove(i);
    //            }
    //        }
    //        if (!notHit) {
    //            noBillingTimes++;
    //        } else {
    //            billingTimes++;
    //        }
    //
    //    }
    //
    //
    //    System.out.println("collect = " + collect);
    //    System.out.println("noBillingTimes = " + noBillingTimes);
    //    System.out.println("billingTimes = " + billingTimes);
    //    System.out.println("interval = " + interval);
    //}
}
